package com.tfswx.fljdmxyy.taskContext;

import cn.hutool.core.collection.CollectionUtil;
import com.tfswx.fljdmxyy.model.RwXtPz;
import com.tfswx.fljdmxyy.service.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.stream.Collectors;

/**
 * 任务配置
 *
 * @Author 陈文佳
 * @Date 2023/03/24
 */
@Slf4j
@Component
public class TaskConfigurer implements SchedulingConfigurer {

    private final ConcurrentHashMap<String, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>();

    private final ConcurrentHashMap<String, CronTask> cronTasks = new ConcurrentHashMap<>();

    private volatile ScheduledTaskRegistrar registrar;

    @Resource
    private TaskService taskService;

    @Override
    public void configureTasks(ScheduledTaskRegistrar registrar) {
        registrar.setScheduler(Executors.newScheduledThreadPool(20));
        this.registrar = registrar;
    }

    @PreDestroy
    public void destroy() {
        this.registrar.destroy();
    }

    /**
     * @MonthName： refreshTask
     * ： 初始化任务
     * 1、从数据库获取执行任务的集合【TaskConfig】
     * 2、通过调用 【refresh】 方法刷新任务列表
     * 3、每次数据库中的任务发生变化后重新执行【1、2】
     * @Author： 陈文佳
     * @Date： 2022/2/25 9:42
     * @Param： [tasks]
     * @return： void
     **/
    public void refreshTask(List<RwXtPz> tasks) {
        // 1、如果数据库查询出的任务为空，则清除所有内存中的定时任务，并退出
        if (CollectionUtil.isEmpty(tasks)) {
            scheduledFutures.keySet().forEach(key -> {
                scheduledFutures.get(key).cancel(false);
                scheduledFutures.remove(key);
                cronTasks.remove(key);
            });
            return;
        }

        // 2、如果数据库查询出的任务列表"tasks"不为空，则移除不在"tasks"中的定时任务
        List<String> rwbhList = tasks.stream().map(RwXtPz::getRwbh).collect(Collectors.toList());
        scheduledFutures.keySet().stream()
                        .filter(key -> !CollectionUtil.contains(rwbhList, key))
                        .forEach(key -> {
                            scheduledFutures.get(key).cancel(false);
                            scheduledFutures.remove(key);
                            cronTasks.remove(key);
                        });

        // 3、添加新任务、更改执行规则任务
        tasks.forEach(item -> {
            String expression = item.getRwbds();
            // 任务表达式为空则跳过
            if (StringUtils.isEmpty(expression)) {
                return;
            }
            // 表达式格式错误则跳过
            if (!CronExpression.isValidExpression(expression)) {
                log.info("====执行单个任务，任务ID【{}】，cron表达式格式错误，cron表达式【{}】=======", item.getRwbh(), expression);
                return;
            }
            // 任务已存在并且表达式未发生变化则跳过
            if (scheduledFutures.containsKey(item.getRwbh()) && cronTasks.get(item.getRwbh()).getExpression().equals(expression)) {
                return;
            }

            // 任务执行时间发生了变化，则删除该任务
            if (scheduledFutures.containsKey(item.getRwbh())) {
                scheduledFutures.get(item.getRwbh()).cancel(false);
                scheduledFutures.remove(item.getRwbh());
                cronTasks.remove(item.getRwbh());
            }

            CronTask task = new CronTask(new Runnable() {
                @Override
                public void run() {
                    // 执行业务逻辑
                    try {
                        log.info("====执行单个任务，任务ID【{}】，执行规则【{}】=======", item.getRwbh(), item.getRwbds());
                        taskService.execute(item);
                    } catch (Exception e) {
                        log.error("执行任务异常，异常信息：{}", e);
                    }
                }
            }, expression);
            ScheduledFuture<?> future = registrar.getScheduler().schedule(task.getRunnable(), task.getTrigger());
            cronTasks.put(item.getRwbh(), task);
            scheduledFutures.put(item.getRwbh(), future);
        });
    }

    private Boolean exists(List<RwXtPz> tasks, String rwbh) {
        for (RwXtPz task : tasks) {
            if (StringUtils.equals(task.getRwbh(), rwbh)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 停止定时任务
     *
     * @param className
     */
    public void stop(String className) {
        if (scheduledFutures.get(className) != null) {
            while (!scheduledFutures.get(className).isDone()) {
                break;
            }
            scheduledFutures.get(className).cancel(true);
            scheduledFutures.remove(className);
        }
    }
}
